#include "SpaceShooterClient.h"
#include <cstring>

SpaceShooterClient* gGame = 0;

SpaceShooterClient::SpaceShooterClient(HINSTANCE hInstance, std::string caption, D3DDEVTYPE devType, DWORD requestedVP)
	:
d3dapplication(hInstance, caption, devType, requestedVP)
{
	srand((unsigned int)time(NULL));

	// make these NULL for safety's sake
	mpD3DSprite = NULL;
	mpD3DFont = NULL;

	// get the device capabilities. If this fails, there's
	// no device we can work with.
	if(!checkDeviceCaps())
	{
		MessageBox(0, "checkDeviceCaps() Failed", 0, 0);
		PostQuitMessage(0);
	}

	// initialize vertex declarations which we will use
	// in the shader
	InitAllVertexDeclarations();

	// init the game vars
	InitializeGameVariables();

	// initialize the floating text manager
	TEXTMANAGER->Initialize();

	// initialize the sound engine
	SOUNDMANAGER->Initialize();

	// load basic game assets
	LoadAssets();

	mState = GAMESTATE_GETIPADDRESS;

	mIPAddress = "";
	
}

///////////////////////////////
// Destructor
//
// Releases all memory assets
///////////////////////////////
SpaceShooterClient::~SpaceShooterClient()
{
	mpD3DSprite->Release();
	mpD3DFont->Release();

	SOUNDMANAGER->Release();
	VIEW->ClearAll();

	DestroyAllVertexDeclarations();
}

///////////////////////////////
// checkDeviceCaps()
// 
// Checks the D3D device capabilities
///////////////////////////////
bool SpaceShooterClient::checkDeviceCaps()
{
	D3DCAPS9 caps;
	HR(gD3DDevice->GetDeviceCaps(&caps));

	// Check for vertex shader version 2.0 support.
	if( caps.VertexShaderVersion < D3DVS_VERSION(2, 0) )
		return false;

	// Check for pixel shader version 2.0 support.
	if( caps.PixelShaderVersion < D3DPS_VERSION(2, 0) )
		return false;

	// Check render target support.  The adapter format can be either the display mode format
	// for windowed mode, or D3DFMT_X8R8G8B8 for fullscreen mode, so we need to test against
	// both.  We use D3DFMT_R32F as the render texture format and D3DFMT_D24X8 as the 
	// render texture depth format.
	D3DDISPLAYMODE mode;
	mD3DObject->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode);

	// Windowed.
	if(FAILED(mD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, mDevType, mode.Format, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_R32F)))
		return false;
	if(FAILED(mD3DObject->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, mDevType, mode.Format,	D3DFMT_R32F, D3DFMT_D24X8)))
		return false;

	// Fullscreen.
	if(FAILED(mD3DObject->CheckDeviceFormat(D3DADAPTER_DEFAULT, mDevType, D3DFMT_X8R8G8B8, D3DUSAGE_RENDERTARGET, D3DRTYPE_TEXTURE, D3DFMT_R32F)))
		return false;
	if(FAILED(mD3DObject->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, mDevType, D3DFMT_X8R8G8B8,	D3DFMT_R32F, D3DFMT_D24X8)))
		return false;


	return true;
}

////////////////////////////////////////////////
// onLostDevice()
//
// Performs necessary D3DX function calls when the
// d3d device is lost
////////////////////////////////////////////////
void SpaceShooterClient::OnLostDevice()
{
	mpD3DSprite->OnLostDevice();
	mpD3DFont->OnLostDevice();
	VIEW->OnLostDevice();
	TEXTMANAGER->OnLostDevice();
}

////////////////////////////////////////////////
// onResetDevice()
//
// Performs necessary D3DX function calls when teh
// d3d device is reset
////////////////////////////////////////////////
void SpaceShooterClient::OnResetDevice()
{
	mpD3DSprite->OnResetDevice();
	mpD3DFont->OnResetDevice();
	VIEW->OnResetDevice();
	VIEW->InitializeProjectionMatrix(mD3DPP.BackBufferWidth, mD3DPP.BackBufferHeight);
	mUI->OnResetDevice(mD3DPP.BackBufferWidth, mD3DPP.BackBufferHeight);
	TEXTMANAGER->OnResetDevice(mD3DPP.BackBufferWidth, mD3DPP.BackBufferHeight);
}

////////////////////////////////////////////////
// Render()
// 
// Renders all objects to the screen
////////////////////////////////////////////////
void SpaceShooterClient::Render()
{
	// begin rendering the scene
	VIEW->BeginScene();

	static char buffer[TEXT_BUFFERSIZE];

	switch(mState)
	{
	case GAMESTATE_GETIPADDRESS:
			GetIPAddressRender();
			break;

	case GAMESTATE_PLAY:


		// loop through each object rendering their location
		if (mGalMapOpen)
		{
			for(int i = 0; i < gGridSize; ++i)
			{
				for(int j = 0; j < gGridSize; ++j)
				{
					VIEW->RenderLRSInfo(((Sector*)mGalaxyMap.GetNode(i, j))->GetInfo(), i, j, mpD3DFont);

					// render the galaxy map grid piece at this location
					VIEW->RenderObject(OBJECTTYPE_GALAXYGRID, i, j);
				
				}
			}
			D3DXVECTOR3 pos = ConvertCoordinateToD3DXVector3(mPlayerInfo.currentSector, PLAYERPOS_Y);
			sprintf_s(buffer, "You are here\n|\nV");
			TEXTMANAGER->DrawInstantText(mpD3DFont, true, buffer, TEXTSCALE_PLAYERPOS, pos, TEXTCOLOR_PLAYERPOS);
		}
		else
		{
			// render skybox first
			VIEW->RenderSkyBox();

			// render the sector map
			for(int i = 0; i < gGridSize; ++i)
			{
				for(int j = 0; j < gGridSize; ++j)
				{
					GameObject* obj = ((Subsector*)mSectorMap.GetNode(i, j))->GetGameObject();
					if (obj)
					{
						VIEW->RenderObject(obj->GetType(), i, j);
						RenderHoverText(obj, i, j);
					}

					// check current battle status and render the appropriate grid piece
					RenderSectorGridComponents(i, j);
				}
			}

			if (!mUI->HasFocus())
				RenderPathToCursor();
		}

		// render floating text before the UI
		TEXTMANAGER->Render();

		mUI->Render();


		DrawDebugText();

		break;

	case GAMESTATE_SHUTTINGDOWN:
		TEXTMANAGER->DrawInstantText(mpD3DFont, false, "ERROR: Server down or wrong IP address", 3, D3DXVECTOR3(mD3DPP.BackBufferWidth/2, mD3DPP.BackBufferHeight/2, 0), D3DCOLOR_XRGB(255, 0, 0));
		break;

	default:
		// unknown state!
		break;
	}
	
	// stop rendering the scene
	VIEW->EndScene();
}

////////////////////////////////////////////////
// RenderSectorGridComponents()
//
// Renders a sector grid component at this location
// based on the current battle status
////////////////////////////////////////////////
void SpaceShooterClient::RenderSectorGridComponents(int x, int y)
{
	// how many enemies are in this sector
	if (((Sector*)mGalaxyMap.GetNode(mPlayerInfo.currentSector.x, mPlayerInfo.currentSector.y))->GetInfo().numEnemies > 0)
	{
		VIEW->RenderObject(OBJECTTYPE_SECTORGRID_RED, x, y);
	} 
	else if ((mPlayerInfo.blasterEnergy + mPlayerInfo.energyReserve + mPlayerInfo.shieldEnergy) < 600) // 20% of max
	{
		VIEW->RenderObject(OBJECTTYPE_SECTORGRID_YELLOW, x, y);
	}
	else
	{
		VIEW->RenderObject(OBJECTTYPE_SECTORGRID_GREEN, x, y);
	}
}

////////////////////////////////////////////////
// RenderPathToCursor()
//
// iterates through the list obtained by the A*
// algorithm earlier, rendering highlight objects
// on the map
////////////////////////////////////////////////
void SpaceShooterClient::RenderPathToCursor(void)
{
	// skip this if nothing to render
	if (mHighlightPath.size() <= 0)
		return;

	for(std::vector<Node*>::iterator i = mHighlightPath.begin(); i != mHighlightPath.end(); ++i)
	{
		Coordinate pos = (*i)->GetCoordinate();
		VIEW->RenderObject(OBJECTTYPE_SECTORGRID_HIGHLIGHT, pos.x, pos.y);
	}

}

////////////////////////////////////////////////
// Update()
//
// The main update command
////////////////////////////////////////////////
void SpaceShooterClient::Update(float dt)
{
	static float counter = 0.0f;
	static int count = 0;
	static bool moved = false;

	static float shutdowncounter = 0.0f;

	Message* msg = 0;

	switch(mState)
	{
	case GAMESTATE_GETIPADDRESS:
		GetIPAddressUpdate();
		break;

	case GAMESTATE_PLAY:


		counter += dt;

		//check for user input
		CheckInput(dt) ;

		// update the game input
		gameInput->poll();

		TEXTMANAGER->Update(dt);

		msg = CLIENTINTERFACE->ReceiveFromServer();

		if (msg)
		{
			ProcessMessage(msg);
		}

		UpdateCameraPosition();

		// update the message delays
		if (mErrorMessageDelay > 0)
			mErrorMessageDelay -= dt;
		else
			mErrorMessageDelay = 0;

		if (mNotifyMessageDelay > 0)
			mNotifyMessageDelay -= dt;
		else
			mNotifyMessageDelay = 0;

		break;

	case GAMESTATE_SHUTTINGDOWN:
		// just wait
		shutdowncounter += dt;
		if (shutdowncounter > SHUTDOWN_WAITTIME)
			PostMessage(mhMainWnd, WM_QUIT, 0, 0); // tell the process to end
		break;

	default:
		// unknown state!
		break;
	}
}

////////////////////////////////////////////////
// CheckInput()
// 
// Checks for user input
////////////////////////////////////////////////
void SpaceShooterClient::CheckInput(float dt)
{
	//WASD Keys and cursor position vector: CAMERA PAN - moves camera along the x and z axis. y is always 0
	if (gameInput->keyDown(DIK_UP) ||
		gameInput->keyDown(DIK_DOWN) ||
		gameInput->keyDown(DIK_LEFT) ||
		gameInput->keyDown(DIK_RIGHT) ||
		gameInput->keyDown(DIK_W) ||
		gameInput->keyDown(DIK_S) ||
		gameInput->keyDown(DIK_A) ||
		gameInput->keyDown(DIK_D))
	{
		//get distance between camera position and lookAt target
		
		D3DXVECTOR3 distance;

		if (mGalMapOpen)
		{
			distance = mGalaxyCameraTarget - VIEW->GetCameraPos();
		}
		else
		{
			distance = mCameraTarget - VIEW->GetCameraPos();
		}

		//normalize distance
		D3DXVec3Normalize(&distance, &distance);
		//y is always 0
		distance.y = NULL;

		if (gameInput->keyDown(DIK_UP) || gameInput->keyDown(DIK_W))
		{
			//get new position of target
			if (mGalMapOpen)
			{
				mGalaxyCameraTarget += distance * CAMERA_SPEED_MOVE; // add
			}
			else
			{
				mCameraTarget += distance * CAMERA_SPEED_MOVE; // add
			}
			
		}

		if (gameInput->keyDown(DIK_DOWN) || gameInput->keyDown(DIK_S))
		{
			//get new position of target
			if (mGalMapOpen)
			{
				mGalaxyCameraTarget -= distance * CAMERA_SPEED_MOVE; // subtract
			}
			else
			{
				mCameraTarget -= distance * CAMERA_SPEED_MOVE; // subtract
			}
			
		}

		if (gameInput->keyDown(DIK_LEFT) || gameInput->keyDown(DIK_A))
		{
			//flip x and z values
			float temp = distance.x ;
			distance.x = -distance.z ;
			distance.z = temp ;

			if (mGalMapOpen)
			{
				mGalaxyCameraTarget += distance * CAMERA_SPEED_MOVE ;	// add
			}
			else
			{
				mCameraTarget += distance * CAMERA_SPEED_MOVE ;	// add
			}
			
		}

		if (gameInput->keyDown(DIK_RIGHT) || gameInput->keyDown(DIK_D))
		{
			//flip x and z values
			float temp = distance.x ;
			distance.x = -distance.z ;
			distance.z = temp ;

			if (mGalMapOpen)
			{
				mGalaxyCameraTarget -= distance * CAMERA_SPEED_MOVE ; // subtract
			}
			else
			{
				mCameraTarget -= distance * CAMERA_SPEED_MOVE ; // subtract	
			}
		}
	}

	//Right-click mouse button
	if(gameInput->mouseButtonDown(1))
	{
		if (mGalMapOpen)
		{
			mGalaxyCameraRotationY += gameInput->mouseDX() * CAMERA_SPEED_ROTATION;
		}
		else
		{
			mCameraRotationY += gameInput->mouseDX() * CAMERA_SPEED_ROTATION;
		}
	}

	//Mouse Scroll - zoom in/out
	if (mGalMapOpen)
	{
		mGalaxyCameraRadius -= gameInput->mouseDZ() * CAMERA_SPEED_ZOOM;
	}
	else
	{
		mCameraRadius -= gameInput->mouseDZ() * CAMERA_SPEED_ZOOM;
	}

	if (gameInput->mouseButtonDown(0) && mLMBReady)
	{
		mLMBPressed = true;
		mLMBReady = false;
	}
	else
	{
		mLMBPressed = false;
	}
	
	if (!gameInput->mouseButtonDown(0))
	{
		mLMBReady = true;
	}

	// figure out if the player clicked on any menu items
	POINT mousePos;
	GetCursorPos(&mousePos);
	ScreenToClient(mhMainWnd, &mousePos);

	mUI->Update(mousePos, mLMBPressed);

	// find out where the cursor is on the game board
	D3DXVECTOR3 cursorPos = GetWorldPickingRay();

	// convert the world coordinates, to grid coordinates so
	// we can access the Node in the array.
	Coordinate gridPos;
	gridPos.x = (int)((cursorPos.x) / WORLD_X_SCALE);
	gridPos.y = (int)((cursorPos.z * -1.0f) / WORLD_Y_SCALE);

	// if the sector map is open, do an A* algorithm and save the result for rendering
	if(!mGalMapOpen)
	{
		// gridpos is the new coordinate, mMouseCoordinate is the previous one
		// if they're the same, don't do the A* algorithm
		if (gridPos.x != mMouseCoordinate.x || gridPos.y != mMouseCoordinate.y)
		{
			mHighlightPath = mSectorMap.FindPath(mPlayerInfo.currentSubSector, gridPos);
		}
	}

	// update the coordinate
	mMouseCoordinate = gridPos;

	// player clicked. respond!
	if (mLMBPressed)
	{
		if (!mUI->HasFocus())
		{
			Node* test;

			// check the current map
			if (mGalMapOpen)
			{
				test = mGalaxyMap.GetNode(mMouseCoordinate.x, mMouseCoordinate.y);
				if (test)
				{
					Event(MSGTYPE_INPUT_HYPERDRIVE_TO_COORDINATE);
				}
			}
			else
			{
				test = mSectorMap.GetNode(mMouseCoordinate.x, mMouseCoordinate.y);
				if (test)
				{
					Event(MSGTYPE_INPUT_LEFT_CLICKED_COORDINATE);
				}
			}

		}
	}

	
}

////////////////////////////////////////////////
// DrawDebugText()
//
// draw custom text on the screen for debugging
////////////////////////////////////////////////
void SpaceShooterClient::DrawDebugText(void)
{

	static char buffer[256];

	sprintf_s(buffer, "%i", mUIEnabled);

	RECT R = {0, 0, 0, 0};

	mpD3DFont->DrawTextA(0, buffer, -1, &R, DT_NOCLIP, D3DCOLOR_ARGB(255, 0, 255, 0));
	
}
////////////////////////////////////////////////
// Event
//
// Sent by other objects (e.g UI buttons)
// to inform the client to send a message to the server
////////////////////////////////////////////////
void SpaceShooterClient::Event(MessageType msgType)
{
	// initialize the different messages we'll need
	// since it's a switch statement
	Message m;
	Msg_HasCoordinate hc;

	// only process the event if the UI is enabled
	if (mUIEnabled)
	{
		bool disableUI = true;

		switch(msgType)
		{
			// fallthrough
		case MSGTYPE_INPUT_INCREASE_BLASTERS:
		case MSGTYPE_INPUT_DECREASE_BLASTERS:
		case MSGTYPE_INPUT_INCREASE_SHIELDS:
		case MSGTYPE_INPUT_DECREASE_SHIELDS:
		case MSGTYPE_INPUT_FIRE_BLASTERS:
		case MSGTYPE_INPUT_UNDOCK:
		case MSGTYPE_INPUT_WAIT_ONE_TURN:
		case MSGTYPE_INPUT_RESTART:
			m.mType = msgType;
			CLIENTINTERFACE->SendToServer(&m);
			break;

		case MSGTYPE_INPUT_HYPERDRIVE_TO_COORDINATE:
			// intercept this message and check if the hyperdrive is disabled
			// otherwise, fallthrough
			if (mPlayerInfo.turnsUntilRepairHyperdrive > 0)
			{
				m.mType = MSGTYPE_ERROR_HYPERDRIVE_DAMAGED;
				// send error message to game to display that hyperdrive is broken
				ProcessMessage(&m);
				disableUI = false; // don't disable the UI in this case, since we aren't involving the server
				break;
			}
			// otherwise, fallthrough and send a message to the server
			// notifying them that we just clicked here.
			// DO NOT PUT A BREAK HERE
		case MSGTYPE_INPUT_LEFT_CLICKED_COORDINATE:
			hc.mType = msgType;
			hc.mCoord = mMouseCoordinate;
			CLIENTINTERFACE->SendToServer(&hc);
			break;

		default:
			// UNKNOWN MESSAGE TYPE!
			break;
		}

		// disable UI instructions until further notice
		if (disableUI)
			mUIEnabled = false;

	}

	
}

////////////////////////////////////////////////
// ProcessMessage
//
// Processes message received from the server
////////////////////////////////////////////////
void SpaceShooterClient::ProcessMessage(Message* msg)
{
	// the different points we're going to need, since its a switch statement
	Msg_HasLRS* lrs;
	Msg_HasCoordinate* hc;
	Msg_HasCoordinateAndObject* hcao;
	Msg_HasCoordinateAndInt* hcai;
	Msg_HasPlayerInfo* hpi;
	Msg_HasInt* hi;
	Node* node;
	GameObject* obj;
	CombatUnit* cu;
	ObjectType objType;
	D3DXVECTOR3 pos;

	// buffer for rendering text
	static char buffer[TEXT_BUFFERSIZE];

	switch(msg->mType)
	{
		case MSGTYPE_OUTPUT_LRS_INFORMATION:
			// typecast the message so we can understand it
			lrs = (Msg_HasLRS*)msg;
			// pass the LRS info into the galaxy map to populate the data
			mGalaxyMap.SetObject(lrs->mLRSInfo, lrs->mCoord.x, lrs->mCoord.y);
			break;

		case MSGTYPE_OUTPUT_ENTER_SECTOR_REMOVE_EXISTING_OBJECTS:
			// delete everything from the sector map
			mSectorMap.ClearAll();
			break;

		case MSGTYPE_OUTPUT_POSITION_PLAYER_SHIP_AT_COORDINATE:
			// typecast the message so we can understand it
			hc = (Msg_HasCoordinate*)msg;
			// grab the node where the player currently is
			node = mSectorMap.GetNode(mPlayerInfo.currentSubSector.x, mPlayerInfo.currentSubSector.y);
			// convert it to a subsector pointer and destroy the object there
			((Subsector*)node)->DestroyGameObject(); // verifies itself if the object already exists before deleting it
			// create a new playership
			obj = new Generic;
			obj->SetType(OBJECTTYPE_PLAYERSHIP);
			// put the player ship in the correct location
			mSectorMap.SetObject(obj, hc->mCoord.x, hc->mCoord.y);
			// update player info with the new location
			mPlayerInfo.currentSubSector = hc->mCoord;
			// update the highlighted path with the A* algorithm
			mHighlightPath = mSectorMap.FindPath(mPlayerInfo.currentSubSector, mMouseCoordinate);
			// turn off the galaxy map
			CloseGalaxyMap();
			break;

		case MSGTYPE_OUTPUT_CREATE_GAME_OBJECT_AT_COORDINATE:
			hcao = (Msg_HasCoordinateAndObject*)msg;
			CreateObject(hcao->mObjType, hcao->mCoord.x, hcao->mCoord.y);
			break;

		case MSGTYPE_OUTPUT_DESTROY_GAME_OBJECT_AT_COORDINATE:
			hcao = (Msg_HasCoordinateAndObject*)msg;
			if (hcao->mObjType == OBJECTTYPE_MISSILE)
			{
				sprintf_s(buffer, "Missed: Missile struck a star!");
				pos = ConvertCoordinateToD3DXVector3(hcao->mCoord.x, hcao->mCoord.y);
				pos.y = RISINGTEXT_STARTY;
				TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_GENERAL, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_PLAYERDAMAGED);
			}
			else if (hcao->mObjType == OBJECTTYPE_SPACESTATION)
			{
				sprintf_s(buffer, "Missed: Missile struck a space station!");
				pos = ConvertCoordinateToD3DXVector3(hcao->mCoord.x, hcao->mCoord.y);
				pos.y = RISINGTEXT_STARTY;
				TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_GENERAL, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_PLAYERDAMAGED);
			}
			else
			{
				mSectorMap.DeleteObject(hcao->mCoord);
			}
			break;

		case MSGTYPE_OUTPUT_MISSILE_HIT_AT_COORDINATE:
			hc = (Msg_HasCoordinate*)msg;
			sprintf_s(buffer, "Missile hit!");
			pos = ConvertCoordinateToD3DXVector3(hc->mCoord.x, hc->mCoord.y);
			TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_GENERAL, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_GENERAL);
			break;

		case MSGTYPE_OUTPUT_MISSILE_MISS_AT_COORDINATE:
			hc = (Msg_HasCoordinate*)msg;
			sprintf_s(buffer, "Missile missed!");
			pos = ConvertCoordinateToD3DXVector3(hc->mCoord.x, hc->mCoord.y);
			TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_GENERAL, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_GENERAL);
			break;

		case MSGTYPE_OUTPUT_ENEMY_ENABLED_COMBAT_MODE_AT_COORDINATE:
			hc = (Msg_HasCoordinate*)msg;
			obj = ((Subsector*)mSectorMap.GetNode(hc->mCoord.x, hc->mCoord.y))->GetGameObject();
			if (obj)
			{
				if (obj->GetType() == OBJECTTYPE_ENEMYSHIP)
				{
					((CombatUnit*)obj)->EnableCombatMode();
				}
				else
				{
					// ENEMY IS NOT HERE
					// ERROR: OUT OF SYNC WITH SERVER
				}
			}
			break;

		case MSGTYPE_OUTPUT_ENEMY_SHIP_DAMAGED_AT_COORDINATE:
			hcai = (Msg_HasCoordinateAndInt*)msg;
			obj = ((Subsector*)mSectorMap.GetNode(hcai->mCoord.x, hcai->mCoord.y))->GetGameObject();
			if (obj)
			{
				if (obj->GetType() == OBJECTTYPE_ENEMYSHIP)
				{
					int energy = ((CombatUnit*)obj)->GetEnergy();
					((CombatUnit*)obj)->SetEnergy(energy - hcai->mInteger);
					sprintf_s(buffer, "-%i", hcai->mInteger);
					pos = ConvertCoordinateToD3DXVector3(hcai->mCoord.x, hcai->mCoord.y);
					pos.y = RISINGTEXTHIGHER_STARTY;
					TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_ENEMYDAMAGED, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_ENEMYDAMAGED);
				}
				else
				{
					// ENEMY IS NOT HERE
					// ERROR: OUT OF SYNC WITH SERVER
				}
			}
			break;

		case MSGTYPE_OUTPUT_ENEMY_SHIP_CONSUMED_ENERGY_AT_COORDINATE:
			hcai = (Msg_HasCoordinateAndInt*)msg;
			obj = ((Subsector*)mSectorMap.GetNode(hcai->mCoord.x, hcai->mCoord.y))->GetGameObject();
			if (obj->GetType() == OBJECTTYPE_ENEMYSHIP)
			{
				int energy = ((CombatUnit*)obj)->GetEnergy();
				((CombatUnit*)obj)->SetEnergy(energy - hcai->mInteger);
				sprintf_s(buffer, "-%i", hcai->mInteger);
				pos = ConvertCoordinateToD3DXVector3(hcai->mCoord.x, hcai->mCoord.y);
				pos.y = RISINGTEXT_STARTY;
				TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_ENEMYATTACKED, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_ENEMYATTACKED);
			}
			else
			{
				// ENEMY IS NOT HERE
				// ERROR: OUT OF SYNC WITH SERVER
			}
			break;

		case MSGTYPE_OUTPUT_MOVED_PLAYER_TOOK_DAMAGE_AT_COORDINATE:
			// find out what object it was and send the proper message
			hcai = (Msg_HasCoordinateAndInt*)msg;
			objType = (((Subsector*)mSectorMap.GetNode(hcai->mCoord.x, hcai->mCoord.y))->GetGameObject())->GetType();
			if (objType == OBJECTTYPE_ENEMYSHIP)
			{
				sprintf_s(buffer, "-%i\nAttack of opportunity", hcai->mInteger);
				
			}
			else if (objType == OBJECTTYPE_STAR)
			{
				sprintf_s(buffer, "-%i\nEnvironmental damage", hcai->mInteger);
			}
			else
			{
				// UNKNOWN OBJECT IS HERE!
				// ERROR: OUT OF SYNC WITH SERVER
			}
			pos = ConvertCoordinateToD3DXVector3(hcai->mCoord.x, hcai->mCoord.y);
			pos.y = RISINGTEXT_STARTY;
			TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_PLAYERDAMAGED, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_PLAYERDAMAGED);
			break;

		case MSGTYPE_OUTPUT_PLAYER_DOCKED_AT_COORDINATE:
			// render some text informing them they are docked
			hc = (Msg_HasCoordinate*)msg;
			sprintf_s(buffer, "You have docked with the space station and resupplied. Use the End Turn button to repair (if needed).");
			pos = ConvertCoordinateToD3DXVector3(hc->mCoord);
			pos.y = RISINGTEXT_STARTY;
			TEXTMANAGER->AddRisingText(mpD3DFont, true, buffer, RISINGTEXT_LIFETIME, TEXTSCALE_GENERAL, pos, RISINGTEXT_VELOCITY, TEXTCOLOR_GENERAL);
			break;

		case MSGTYPE_OUTPUT_SET_PLAYER_STATUS:
			hpi = (Msg_HasPlayerInfo*)msg;
			mPlayerInfo = hpi->mPlayerInfo;
			break;

		case MSGTYPE_OUTPUT_MOVED_RESERVES_WERE_DRAINED:
			hi = (Msg_HasInt*)msg;
			sprintf_s(buffer, "During your movement you consumed %i energy from reserves.", hi->mInteger);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, NOTIFYMSG_LIFETIME, NOTIFYMSG_SCALE, NOTIFYMSG_POS, NOTIFYMSG_VEL, NOTIFYMSG_COLOR, mNotifyMessageDelay);
			mNotifyMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_MOVED_SHIELDS_WERE_DRAINED:
			hi = (Msg_HasInt*)msg;
			sprintf_s(buffer, "Not enough energy to move. %i energy was drained from shields instead.", hi->mInteger);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, NOTIFYMSG_LIFETIME, NOTIFYMSG_SCALE, NOTIFYMSG_POS, NOTIFYMSG_VEL, NOTIFYMSG_COLOR, mNotifyMessageDelay);
			mNotifyMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_MOVED_BLASTERS_WERE_DRAINED:
			hi = (Msg_HasInt*)msg;
			sprintf_s(buffer, "Not enough energy to move. %i energy was drained from blasters instead.", hi->mInteger);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, NOTIFYMSG_LIFETIME, NOTIFYMSG_SCALE, NOTIFYMSG_POS, NOTIFYMSG_VEL, NOTIFYMSG_COLOR, mNotifyMessageDelay);
			mNotifyMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_ATTACKED_SHIELDS_WERE_DRAINED:
			hi = (Msg_HasInt*)msg;
			sprintf_s(buffer, "Your shields absorbed %i damage from attacks.", hi->mInteger);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, NOTIFYMSG_LIFETIME, NOTIFYMSG_SCALE, NOTIFYMSG_POS, NOTIFYMSG_VEL, NOTIFYMSG_COLOR, mNotifyMessageDelay);
			mNotifyMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_ATTACKED_BLASTERS_WERE_DRAINED:
			hi = (Msg_HasInt*)msg;
			sprintf_s(buffer, "There wasn't enough shield energy to absorb the attack. %i emergency energy was drained from the blasters.", hi->mInteger);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, NOTIFYMSG_LIFETIME, NOTIFYMSG_SCALE, NOTIFYMSG_POS, NOTIFYMSG_VEL, NOTIFYMSG_COLOR, mNotifyMessageDelay);
			mNotifyMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_ATTACKED_RESERVES_WERE_DRAINED:
			hi = (Msg_HasInt*)msg;
			sprintf_s(buffer, "There wasn't enough shield energy to absorb the attack. %i emergency energy was drained from reserves.", hi->mInteger);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, NOTIFYMSG_LIFETIME, NOTIFYMSG_SCALE, NOTIFYMSG_POS, NOTIFYMSG_VEL, NOTIFYMSG_COLOR, mNotifyMessageDelay);
			mNotifyMessageDelay += MESSAGE_DELAY;
			break;

			// create a floating text object in the middle of the screen
		case MSGTYPE_ERROR_CAN_NOT_MOVE_THERE:
			sprintf_s(buffer, "Error: Cannot move there. Obstacle in the way.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_HYPERDRIVE_DAMAGED:
			sprintf_s(buffer, "Error: Hyperdrive is damaged. %i turns until repairs are complete.", mPlayerInfo.turnsUntilRepairHyperdrive);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_SUBLIGHT_DAMAGED:
			sprintf_s(buffer, "Error: Sublight drive is damaged. %i turns until repairs are complete.", mPlayerInfo.turnsUntilRepairSublight);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_MISSILE_LAUNCHERS_DAMAGED:
			sprintf_s(buffer, "Error: Missile launchers are damaged. %i turns until repairs are complete.", mPlayerInfo.turnsUntilRepairMissiles);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_SHIELDS_DAMAGED:
			sprintf_s(buffer, "Error: Shields are damaged. %i turns until repairs are complete.", mPlayerInfo.turnsUntilRepairShields);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_BLASTERS_DAMAGED:
			sprintf_s(buffer, "Error: Blasters are damaged. %i turns until repairs are complete.", mPlayerInfo.turnsUntilRepairBlaster);
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_NO_MISSILES_REMAINING:
			sprintf_s(buffer, "Error: No missiles remaining. Dock with a space station to resupply.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_CAN_NOT_DOCK_ENEMIES_NEARBY:
			sprintf_s(buffer, "Error: Cannot dock. Enemies nearby.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_NOT_ENOUGH_ENERGY_RESERVES_TO_TRANSFER:
			sprintf_s(buffer, "Error: Not enough energy reserves to transfer.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_NOT_ENOUGH_SHIELDS_TO_TRANSFER:
			sprintf_s(buffer, "Error: Not enough Shield energy to transfer.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_NOT_ENOUGH_BLASTER_ENERGY_TO_TRANSFER:
			sprintf_s(buffer, "Error: Not enough Blaster energy to transfer.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_NO_BLASTER_ENERGY_ALLOCATED:
			sprintf_s(buffer, "Error: No Blaster energy has been allocated.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;
		case MSGTYPE_ERROR_CAN_NOT_HYPERDRIVE_TOO_FAR:
			sprintf_s(buffer, "Error: Cannot Hyperdrive there. Too far.");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, ERRORMSG_LIFETIME, ERRORMSG_SCALE, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_GAME_OVER_WIN:
			sprintf_s(buffer, "GAME OVER - YOU WIN!");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, 10, 5, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_GAME_OVER_LOSE:
			sprintf_s(buffer, "GAME OVER - YOU LOSE!");
			TEXTMANAGER->AddRisingText(mpD3DFont, false, buffer, 10, 5, ERRORMSG_POS, ERRORMSG_VEL, ERRORMSG_COLOR, mErrorMessageDelay);
			mErrorMessageDelay += MESSAGE_DELAY;
			break;

		case MSGTYPE_OUTPUT_DONE:
			mUIEnabled = true; // reenable the UI			
			break;

		default:
			// UNKNOWN MESSAGE TYPE!
			break;
	}
}

void SpaceShooterClient::CreateObject(ObjectType type, int x, int y)
{
	GameObject* obj;
	switch(type)
	{
	case OBJECTTYPE_ENEMYSHIP:
		obj = new CombatUnit;
		obj->SetType(type);
		((CombatUnit*)obj)->SetEnergy(200);
		break;

	case OBJECTTYPE_PLAYERSHIP:
	case OBJECTTYPE_STAR: // fallthrough
	case OBJECTTYPE_SPACESTATION:
		obj = new Generic;
		obj->SetType(type);
		break;
		
	
	default:
		// unknown object
		obj = 0;
		break;
	}

	mSectorMap.SetObject(obj, x, y);
}

////////////////////////////////////////////////
// InitializeGameVariables
//
// Initializes game variables
////////////////////////////////////////////////
void SpaceShooterClient::InitializeGameVariables(void)
{
	mGalMapOpen = false;
	mCameraRotationY = CAMERA_START_ROTATION;
	mCameraRadius = CAMERA_START_RADIUS;
	mCameraTarget = D3DXVECTOR3(CAMERA_INITIAL_TARGET_X, CAMERA_INITIAL_TARGET_Y, CAMERA_INITIAL_TARGET_Z);
	mCameraUp = D3DXVECTOR3(0, 1, 0);
	mGalaxyCameraRotationY = CAMERA_START_ROTATION;
	mGalaxyCameraRadius = CAMERA_START_RADIUS;
	mGalaxyCameraTarget = D3DXVECTOR3(CAMERA_INITIAL_TARGET_X, CAMERA_INITIAL_TARGET_Y, CAMERA_INITIAL_TARGET_Z);
	mLMBReady = true;
	mLMBPressed = false;
	mUIEnabled = true;

	// initialize player info for testing. In the final game, this will be handled by a message
	// from the server
	
	Coordinate pos;
	pos.x = 0;
	pos.y = 0;
	mPlayerInfo.currentSubSector = pos;
	mPlayerInfo.docked = false;
	mPlayerInfo.energyReserve = 2000;
	mPlayerInfo.blasterEnergy = 500;
	mPlayerInfo.shieldEnergy = 500;
	mPlayerInfo.missilesRemaining = 10;
	mPlayerInfo.turnsUntilRepairBlaster = 0;
	mPlayerInfo.turnsUntilRepairHyperdrive = 0;
	mPlayerInfo.turnsUntilRepairShields = 0;
	mPlayerInfo.turnsUntilRepairLRS = 0;
	mPlayerInfo.turnsUntilRepairMissiles = 0;
	mPlayerInfo.turnsUntilRepairSublight = 0;
	mPlayerInfo.turn = 5;
	Coordinate gal;
	gal.x = 0;
	gal.y = 0;
	mPlayerInfo.currentSector = gal;

	// add some objects to render
}

////////////////////////////////////////////////
// LoadAssets()
//
// Loads assets into memory
////////////////////////////////////////////////
void SpaceShooterClient::LoadAssets()
{
	CreateFont();

	LoadMeshes();

	InitializeView();

	mUI = new SpaceShooterMenu(this, mpD3DSprite, mpD3DFont);

	// run this command to get the backbuffer width/height from
	// the size of the window
	OnResetDevice();
}

void SpaceShooterClient::LoadMeshes()
{
	MESHMANAGER->LoadMesh("Assets//Meshes//PlayerShip.X",			OBJECTTYPE_PLAYERSHIP);
	MESHMANAGER->LoadMesh("Assets//Meshes//EnemyShip_Sector.X",		OBJECTTYPE_ENEMYSHIP);
	MESHMANAGER->LoadMesh("Assets//Meshes//EnemyShip_Galaxy.X",		OBJECTTYPE_ENEMYSHIP_GALAXY);
	MESHMANAGER->LoadMesh("Assets//Meshes//SpaceStation_Sector.X",	OBJECTTYPE_SPACESTATION);
	MESHMANAGER->LoadMesh("Assets//Meshes//SpaceStation_Galaxy.X",	OBJECTTYPE_SPACESTATION_GALAXY);
	MESHMANAGER->LoadMesh("Assets//Meshes//Star_Sector.X",			OBJECTTYPE_STAR);
	MESHMANAGER->LoadMesh("Assets//Meshes//Star_Galaxy.X",			OBJECTTYPE_STAR_GALAXY);
	MESHMANAGER->LoadMesh("Assets//Meshes//SectorGrid_Green.X",		OBJECTTYPE_SECTORGRID_GREEN);
	MESHMANAGER->LoadMesh("Assets//Meshes//SectorGrid_Yellow.X",	OBJECTTYPE_SECTORGRID_YELLOW);
	MESHMANAGER->LoadMesh("Assets//Meshes//SectorGrid_Red.X",		OBJECTTYPE_SECTORGRID_RED);
	MESHMANAGER->LoadMesh("Assets//Meshes//SectorGrid_Highlight.X",	OBJECTTYPE_SECTORGRID_HIGHLIGHT);
	MESHMANAGER->LoadMesh("Assets//Meshes//GalaxyGrid.X",			OBJECTTYPE_GALAXYGRID);
	MESHMANAGER->LoadMesh("Assets//Meshes//QuestionMark.X",			OBJECTTYPE_QUESTIONMARK);
}

void SpaceShooterClient::InitializeView()
{
	// create the projection matrix (FOV etc.)
	VIEW->InitializeProjectionMatrix(mD3DPP.BackBufferWidth, mD3DPP.BackBufferHeight);

	// initialize the shader FX
	VIEW->InitializeFX();

	// initialize the view lighting
	VIEW->InitializeLighting();
}

void SpaceShooterClient::CreateFont()
{
	// initialize a sprite graphic
	D3DXCreateSprite(gD3DDevice, &mpD3DSprite);

	// create a font descriptor
	D3DXFONT_DESC fontDesc;
	
	fontDesc.Height          = 18;
	fontDesc.Width           = 8;
	fontDesc.Weight          = 0;
	fontDesc.MipLevels       = 1;
	fontDesc.Italic          = false;
	fontDesc.CharSet         = DEFAULT_CHARSET;
	fontDesc.OutputPrecision = OUT_DEFAULT_PRECIS;
	fontDesc.Quality         = DEFAULT_QUALITY;
	fontDesc.PitchAndFamily  = DEFAULT_PITCH | FF_DONTCARE;
	strcpy_s(fontDesc.FaceName, _T("Times New Roman"));

	// create the font
	D3DXCreateFontIndirect(gD3DDevice, &fontDesc, &mpD3DFont);

}

// GetWorldPickingRay()
// Generates a picking ray based on the current
// camera and mouse cursor position. Since this
// game works in two dimensions we automatically
// extrapolate the ray into a position vector on
// the XZ plane and return it.
D3DXVECTOR3 SpaceShooterClient::GetWorldPickingRay()
{
	// this function takes the position and orientation of the camera and determines
	// the appropriate origin and direction of the ray we will need to figure out
	// which object has been selected

	// Get the clicked screen point.
	POINT s;
	GetCursorPos(&s);           

	// Make it relative to the client area window.
	ScreenToClient(mhMainWnd, &s);

	// By the way we've been constructing things, the entire
	// back buffer is the viewport.
	float w = (float)mD3DPP.BackBufferWidth;
	float h = (float)mD3DPP.BackBufferHeight;

	D3DXMATRIX proj = VIEW->GetProjectionMatrix();

	float x = (2.0f*s.x/w - 1.0f) / proj(0,0);
	float y = (-2.0f*s.y/h + 1.0f) / proj(1,1);

	// Build picking ray in view space.
	D3DXVECTOR3 origin(0.0f, 0.0f, 0.0f);
	D3DXVECTOR3 dir(x, y, 1.0f);

	// So if the view matrix transforms coordinates from
	// world space to view space, then the inverse of the
	// view matrix transforms coordinates from view space
	// to world space.
	D3DXVECTOR3 originW, dirW;
	D3DXMATRIX invView;
	D3DXMatrixInverse(&invView, 0, &VIEW->GetViewMatrix());

	// Transform picking ray to world space.
	D3DXVec3TransformCoord(&originW, &origin, &invView);
	D3DXVec3TransformNormal(&dirW, &dir, &invView);
	D3DXVec3Normalize(&dirW, &dirW);

	// this is enough to generate the ray based on the camera and cursor
	// positions, but for this game we want a vector only on the XZ plane (i.e. y = 0)

	// figure out how many times we need to add the direction
	// vector to the origin to get y to be zero
	float dist = originW.y / dirW.y;

	// Some simple vector math and we have the xz coordinates when
	// y is zero
	D3DXVECTOR3 position = originW - dist * dirW;

	// ta da!
	return position;
}

void SpaceShooterClient::UpdateCameraPosition()
{

	if (mGalMapOpen)
	{
		//Clamps camera radius
		if( mGalaxyCameraRadius > CAMERA_ZOOM_MAX )
			mGalaxyCameraRadius = CAMERA_ZOOM_MAX ;
		if( mGalaxyCameraRadius < CAMERA_ZOOM_MIN )
			mGalaxyCameraRadius = CAMERA_ZOOM_MIN ;
		if (mGalaxyCameraTarget.x < CAMERA_TARGETX_MIN)
			mGalaxyCameraTarget.x = CAMERA_TARGETX_MIN;
		if (mGalaxyCameraTarget.x > CAMERA_TARGETX_MAX)
			mGalaxyCameraTarget.x = CAMERA_TARGETX_MAX;
		if (mGalaxyCameraTarget.z > CAMERA_TARGETZ_MIN)
			mGalaxyCameraTarget.z = CAMERA_TARGETZ_MIN;
		if (mGalaxyCameraTarget.z < CAMERA_TARGETZ_MAX)
			mGalaxyCameraTarget.z = CAMERA_TARGETZ_MAX;

		float x = mGalaxyCameraRadius * sinf(mGalaxyCameraRotationY);
		float y = mGalaxyCameraRadius * cosf(CAMERA_GALAXY_VIEWINGANGLE);
		float z = mGalaxyCameraRadius * cosf(mGalaxyCameraRotationY);

		// update the current view matrix
		D3DXVECTOR3 pos = D3DXVECTOR3(x + mGalaxyCameraTarget.x, y, z + mGalaxyCameraTarget.z);

		VIEW->UpdateView(pos, mGalaxyCameraTarget, mCameraUp);
	}
	else
	{

		//Clamps camera radius
		if( mCameraRadius > CAMERA_ZOOM_MAX )
			mCameraRadius = CAMERA_ZOOM_MAX ;
		if( mCameraRadius < CAMERA_ZOOM_MIN )
			mCameraRadius = CAMERA_ZOOM_MIN ;
		if (mCameraTarget.x < CAMERA_TARGETX_MIN)
			mCameraTarget.x = CAMERA_TARGETX_MIN;
		if (mCameraTarget.x > CAMERA_TARGETX_MAX)
			mCameraTarget.x = CAMERA_TARGETX_MAX;
		if (mCameraTarget.z > CAMERA_TARGETZ_MIN)
			mCameraTarget.z = CAMERA_TARGETZ_MIN;
		if (mCameraTarget.z < CAMERA_TARGETZ_MAX)
			mCameraTarget.z = CAMERA_TARGETZ_MAX;

		float x = mCameraRadius * sinf(mCameraRotationY);
		float y = mCameraRadius * cosf(CAMERA_SECTOR_VIEWINGANGLE);
		float z = mCameraRadius * cosf(mCameraRotationY);

		// update the current view matrix
		D3DXVECTOR3 pos = D3DXVECTOR3(x + mCameraTarget.x, y, z + mCameraTarget.z);

		VIEW->UpdateView(pos, mCameraTarget, mCameraUp);
	}
}

void SpaceShooterClient::RenderHoverText(GameObject* obj, int x, int y)
{
	if(obj->GetType() == OBJECTTYPE_ENEMYSHIP)
	{
		CombatUnit* cu = (CombatUnit*)obj;
		// render text showing the remaining energy on the enemy ship
		D3DXVECTOR3 pos = ConvertCoordinateToD3DXVector3(x, y);
		pos.y += ENERGYTEXT_Y;
		static char text[16];
		sprintf_s(text, "Energy: %i", cu->GetEnergy());
		TEXTMANAGER->DrawInstantText(mpD3DFont, true, text, 1, pos, D3DCOLOR_XRGB(0, 255, 255));
		if (!cu->CombatEnabled())
		{
			pos.y += 8;
			static char text[16];
			sprintf_s(text, "zzz");
			TEXTMANAGER->DrawInstantText(mpD3DFont, true, text, 2, pos, D3DCOLOR_XRGB(255, 255, 255));
		}
	}
}

// functions for getting and rendering the IP address to connect to
void SpaceShooterClient::GetIPAddressUpdate()
{

	gameInput->poll();

	// block these if-statements to save space and make things clear.
	if (gameInput->keyPressed(DIK_1)) {mIPAddress.append("1", 1);}
	if (gameInput->keyPressed(DIK_2)) {mIPAddress.append("2", 1);}
	if (gameInput->keyPressed(DIK_3)) {mIPAddress.append("3", 1);}
	if (gameInput->keyPressed(DIK_4)) {mIPAddress.append("4", 1);}
	if (gameInput->keyPressed(DIK_5)) {mIPAddress.append("5", 1);}
	if (gameInput->keyPressed(DIK_6)) {mIPAddress.append("6", 1);}
	if (gameInput->keyPressed(DIK_7)) {mIPAddress.append("7", 1);}
	if (gameInput->keyPressed(DIK_8)) {mIPAddress.append("8", 1);}
	if (gameInput->keyPressed(DIK_9)) {mIPAddress.append("9", 1);}
	if (gameInput->keyPressed(DIK_0)) {mIPAddress.append("0", 1);}
	if (gameInput->keyPressed(DIK_PERIOD)) {mIPAddress.append(".", 1);}
	if (gameInput->keyPressed(DIK_ESCAPE)) {mIPAddress = "";}

	if (gameInput->keyPressed(DIK_BACKSPACE)) 
	{
		// only erase a character if it's larger than 1 char
		if (mIPAddress.size() > 0)  
			mIPAddress.resize(mIPAddress.size () - 1);
	}
	
	if (gameInput->keyPressed(DIK_RETURN)) 
	{
		// connect to the server
		if (CLIENTINTERFACE->ConnectToServer(mIPAddress.c_str()))
		{
			mState = GAMESTATE_PLAY;
		}
		else
		{
			mState = GAMESTATE_SHUTTINGDOWN;
		}
	}
}

void SpaceShooterClient::GetIPAddressRender()
{
	TEXTMANAGER->DrawInstantText(mpD3DFont, false, "Space Shooter\nby Chris Dickinson and Joe Lang", 2, D3DXVECTOR3(mD3DPP.BackBufferWidth/2, mD3DPP.BackBufferHeight/4, 0), D3DCOLOR_XRGB(200, 175, 50));
	TEXTMANAGER->DrawInstantText(mpD3DFont, false, "Enter the IP address of the server (Esc to clear)", 1.5, D3DXVECTOR3(mD3DPP.BackBufferWidth/2, mD3DPP.BackBufferHeight/3, 0), D3DCOLOR_XRGB(255, 255, 255));
	TEXTMANAGER->DrawInstantText(mpD3DFont, false, (char*)mIPAddress.c_str(), 1, D3DXVECTOR3(mD3DPP.BackBufferWidth/2, mD3DPP.BackBufferHeight/2, 0), D3DCOLOR_XRGB(200, 200, 200));
}

